#ifndef VENDOR_ECARX_IPLM_V1_0_IPLM_H
#define VENDOR_ECARX_IPLM_V1_0_IPLM_H

#include <vendor/ecarx/xma/iplm/1.0/IIplm.h>
#include <vendor/ecarx/xma/iplm/1.0/IIplmCallback.h>
#include <vendor/ecarx/xma/iplm/1.0/types.h>

#include <string>
#include <thread>
#include <mutex>
#include <queue>
#include <map>
#include <vector>
#include <chrono>
#include <bitset>
#include <condition_variable>

#include <net/if.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <netinet/in.h>

#include "Utils.h"
#include "VehicleHelper.h"
#include "Def.h"

namespace vendor {
namespace ecarx {
namespace xma {
namespace iplm {
namespace V1_0 {
namespace implementation {

using ::android::sp;
using ::android::wp;
using ::android::hardware::Void;
using ::android::hardware::Return;
using ::android::hardware::hidl_death_recipient;

using ::vendor::ecarx::xma::iplm::V1_0::IIplm;
using ::vendor::ecarx::xma::iplm::V1_0::IIplmCallback;
using ::vendor::ecarx::xma::iplm::V1_0::ServiceStatus;
using ::vendor::ecarx::xma::iplm::V1_0::IPLM_Preconfigured_Service;
using ::vendor::ecarx::xma::iplm::V1_0::IPLM_ResourceGroup_Index;
using ::vendor::ecarx::xma::iplm::V1_0::IPLM_RG_PRIO;
using ::vendor::ecarx::xma::iplm::V1_0::IPLM_RG_State;
using ::vendor::ecarx::xma::iplm::V1_0::StatusCode;

using CookieType = uint64_t;
class HidlDeathRecipient;

typedef struct {
    bool isCanTriggerIPCP;
    IPLM_RG_State state;
    IPLM_RG_PRIO priority;
    std::vector<ECU_Node> members;
    std::vector<ECU_Node> online_nodes;
    // Key is service + resource_group, value is std::pair
    std::map<std::string, CookieType> cookieMap;
    std::map<CookieType, sp<IIplmCallback>> callbacks;
} Iplm_resource_group;

typedef struct {
    wp<IIplmCallback> what;
    IPLM_ResourceGroup_Index index;
    bool triggerIPCP;
} Callback_Element;

struct Iplm : public IIplm
{
  public:
    Iplm();
    virtual ~Iplm();

    bool initialize();

    Return<StatusCode> setPrecfgServiceStatus(IPLM_Preconfigured_Service service, ServiceStatus status) override;
    Return<StatusCode> subscribe(IPLM_Preconfigured_Service service, IPLM_ResourceGroup_Index resourceGroup, const sp<IIplmCallback>& callback) override; 
    Return<StatusCode> unsubscribe(IPLM_Preconfigured_Service service, IPLM_ResourceGroup_Index resourceGroup) override;
    Return<StatusCode> RequestResourceGroupWithPriority(IPLM_ResourceGroup_Index resourceGroup, IPLM_RG_PRIO priority) override;
    Return<StatusCode> ReleaseResourceGroup(IPLM_ResourceGroup_Index resourceGroup, IPLM_RG_PRIO priority) override;
    Return<IPLM_RG_State> getResourceGroupState(IPLM_ResourceGroup_Index resourceGroup) override;

  private:
    CookieType mCookie;
    int mSocket;
    struct sockaddr_in mBroadcastAddr;

    VehicleHelper mVehicleHelper;

    std::mutex mMiscMx;
    uint32_t mPrecfgServicesMap = static_cast<uint32_t>(IPLM_Preconfigured_Service::ALL_READY);
    int32_t mResourceGroupIndicator;
    int32_t mRequestMonitoringTimeout;

    //mark ihu belong to which resource group
    uint8_t mIhuMark;

    // Map for ip address / ECU_Node_Index, eg. 198.18.32.17 / NODE_TCAM
    std::map<std::string, ECU_Node> mIpAddrNodeMap;

    // IP Activity Message field
    iplm_activity_t mIplmActivity;
    uint16_t mIplmActivityCount;
    uint8_t mIplmActivityBulletin;
    std::mutex mIplmActivityMx;

    // VFC Retrigger Period
    uint32_t mVFCRetriggerBulletin;

    // Core Map for maintaining ResourceGroup State / online members.
    std::mutex mResourceGroupMx;
    std::map<IPLM_ResourceGroup_Index, Iplm_resource_group> mIplmResourceGroupMap;

    // Bzzzik for detecting ECU offline (No external ECU's IP activity message received within mRequestMonitoringTimeout)
    std::mutex mBulletinMx;
    std::map<ECU_Node, uint32_t> mEcuBulletinBoard;

    // Power State field
    // Active Request Session Flag, if this bitset is not 0, POWER_NORMAL would be set.
    std::bitset<16> mARSFlagSet;
    // Power High bitset.
    std::bitset<16> mPriorityHighSet;
    IPLM_Power_State mPowerState;
    std::mutex mResourceGroupStateMx;

    // Thread field
    std::atomic<bool> mIsTerminating;
    std::thread mReceiveThread;
    std::thread mWorkerThread;
    std::thread mCallbackThread;

    // Callback Queue
    std::mutex mCallbackMx;
    std::condition_variable mCallbackCond;
    std::queue<Callback_Element> mCallbackQueue;

    /****************  Private Methods ****************/
    void internalInit();
    void loadConfig();
    bool initTransport();
    bool isAllServiceReady();

    // Receive Thread Handlers
    void doReceive();
    void handleActiveRequest(const iplm_activity_t * packet, ECU_Node node);
    bool sanityCheckIpActivity(iplm_activity_t * packet);
    void updateInternalResourceGroup();

    // Worker Thread Handlers
    void iplmStep();
    void buildIpActivity(const IPLM_ResourceGroup_Index rg, const IPLM_RG_PRIO priority);
    void broadcastIpActivity();
    void releaseIpActivity(const IPLM_ResourceGroup_Index rg, const IPLM_RG_PRIO priority = IPLM_RG_PRIO::PRIO_NORM);

    // Callback Thread Handler
    void doCallback();

    /****************  Helper Methods ****************/
    const char * dumpECU_Name(ECU_Node node);
    const char * dumpPreconfigServiceName(IPLM_Preconfigured_Service service);
    const char * dumpPreconfigServiceStatus(ServiceStatus status);
    const char * dumpResourceGroup(IPLM_ResourceGroup_Index index);

    // handle BigEndian / LittleEndian
    void convertToBigEndian(iplm_activity_t * packet);
    void convertToLittleEndian(iplm_activity_t * packet);

    friend class HidlDeathRecipient;
    sp<HidlDeathRecipient> mDeathRecipient;
    void handleHidlDeath(CookieType cookie);
};

// Death Handler
class HidlDeathRecipient : public hidl_death_recipient {
    public:
        HidlDeathRecipient(const wp<Iplm> host) : mIplm(host) {}

    virtual void serviceDied(uint64_t cookie,
            const wp<::android::hidl::base::V1_0::IBase>& who) {
        sp<Iplm> sp_iplm = mIplm.promote();
        if (sp_iplm != nullptr)
          sp_iplm->handleHidlDeath(cookie);
    }
  private:
    wp<Iplm> mIplm;
};

} // namespace implementation
} // namespace V1_0
} // namespace iplm
} // namespace xma
} // namespace ecarx
} // namespace vendor

#endif /* VENDOR_ECARX_IPLM_V1_0_IPLM_H */
